package modbus

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"time"
)

// Modbus RTU Over TCP Server(Slave)

const (
	maxRTUOverTCPFrameLength int = 256
	RTUOverTCPHeaderLength   int = 2
	CRC16Length              int = 2
)

func BytesToHex(data []byte) string {
	var buffer bytes.Buffer

	for i, b := range data {
		if i > 0 {
			buffer.WriteString(" ") // 在每个16进制数之前添加空格，除了第一个
		}
		buffer.WriteString(fmt.Sprintf("%02X", b)) // 使用%02x确保每个字节都是两位的16进制数
	}

	return buffer.String()
}

type rtuOverTCPTransport struct {
	logger    *logger
	socket    net.Conn
	timeout   time.Duration
	lastTxnId uint16
}

// Returns a new TCP transport.
func newRTUOverTCPTransport(socket net.Conn, timeout time.Duration, customLogger *log.Logger) (tt *rtuOverTCPTransport) {
	tt = &rtuOverTCPTransport{
		socket:  socket,
		timeout: timeout,
		logger:  newLogger(fmt.Sprintf("tcp-transport(%s)", socket.RemoteAddr()), customLogger),
	}

	return
}

// Closes the underlying tcp socket.
func (tt *rtuOverTCPTransport) Close() (err error) {
	err = tt.socket.Close()

	return
}

// Runs a request across the socket and returns a response.
func (tt *rtuOverTCPTransport) ExecuteRequest(req *pdu) (res *pdu, err error) {
	// set an i/o deadline on the socket (read and write)
	err = tt.socket.SetDeadline(time.Now().Add(tt.timeout))
	if err != nil {
		return
	}

	// increase the transaction ID counter
	tt.lastTxnId++

	_, err = tt.socket.Write(tt.assembleMBAPFrame(tt.lastTxnId, req))
	if err != nil {
		return
	}

	res, err = tt.readResponse()

	return
}

// Reads a request from the socket.
func (tt *rtuOverTCPTransport) ReadRequest() (req *pdu, err error) {
	var txnId uint16

	// set an i/o deadline on the socket (read and write)
	err = tt.socket.SetDeadline(time.Now().Add(tt.timeout))
	if err != nil {
		return
	}

	req, txnId, err = tt.readMBAPFrame()
	if err != nil {
		return
	}

	// store the incoming transaction id
	tt.lastTxnId = txnId

	return
}

// Writes a response to the socket.
func (tt *rtuOverTCPTransport) WriteResponse(res *pdu) (err error) {
	_, err = tt.socket.Write(tt.assembleMBAPFrame(tt.lastTxnId, res))
	if err != nil {
		return
	}

	return
}

// Reads as many MBAP+modbus frames as necessary until either the response
// matching tt.lastTxnId is received or an error occurs.
func (tt *rtuOverTCPTransport) readResponse() (res *pdu, err error) {
	var txnId uint16

	for {
		// grab a frame
		res, txnId, err = tt.readMBAPFrame()

		// ignore unknown protocol identifiers
		if err == ErrUnknownProtocolId {
			continue
		}

		// abort on any other erorr
		if err != nil {
			return
		}

		// ignore unknown transaction identifiers
		if tt.lastTxnId != txnId {
			tt.logger.Warningf("received unexpected transaction id "+"(expected 0x%04x, received 0x%04x)", tt.lastTxnId, txnId)
			continue
		}

		break
	}

	return
}

// 192.168.8.210
// Reads an entire frame (MBAP header + modbus PDU) from the socket.
func (tt *rtuOverTCPTransport) readMBAPFrame() (p *pdu, txnId uint16, err error) {
	// fmt.Println("------------------")

	// PDU (Protocol Data Unit，协议数据单元)
	// ADU (Application Data Unit，应用数据单元)

	/*
		// reader := bufio.NewReader(tt.socket)
		var rxbuf []byte
		var unitId uint8
		var functionCode uint8

		recvBuffer, err := io.ReadAll(tt.socket)
		if err != nil {
			fmt.Println("Error reading:", err.Error())
		}
		// 打印接收的数据
		fmt.Println(BytesToHex(recvBuffer))

		unitId = recvBuffer[0]
		functionCode = recvBuffer[1]
		fmt.Printf("SlaveId: %d, FunctionCode: %d\n", unitId, functionCode)

		p = &pdu{
			unitId:       unitId,
			functionCode: functionCode,
			payload:      recvBuffer[2:],
		}
	*/

	var rxbuf []byte
	var unitId uint8       // 从站ID
	var functionCode uint8 // 功能码

	// 先接收2个字节，获取从站ID和功能码
	rxbuf = make([]byte, RTUOverTCPHeaderLength)
	_, err = io.ReadFull(tt.socket, rxbuf)
	if err != nil {
		return
	}
	// 打印接收的数据
	// fmt.Println(BytesToHex(rxbuf))

	unitId = rxbuf[0]
	functionCode = rxbuf[1]
	// fmt.Printf("== SlaveId: %d, FunctionCode: %d ==\n", unitId, functionCode)

	if functionCode == 0x01 || functionCode == 0x02 || functionCode == 0x03 || functionCode == 0x04 { // 请求：读数据

		// fmt.Println("---------------0x01 0x02 0x03 0x04------------------")

		rxbuf = make([]byte, 6)
		_, err = io.ReadFull(tt.socket, rxbuf)
		if err != nil {
			return
		}
		// 打印接收的数据
		// fmt.Println(BytesToHex(rxbuf))

		p = &pdu{
			unitId:       unitId,
			functionCode: functionCode,
			payload:      rxbuf[0:],
		}
	} else if functionCode == 0x05 || functionCode == 0x06 { // 请求：写单个线圈、写单个寄存器

		// fmt.Println("---------------05 06------------------")

		rxbuf = make([]byte, 6)
		_, err = io.ReadFull(tt.socket, rxbuf)
		if err != nil {
			return
		}
		// 打印接收的数据
		// fmt.Println(BytesToHex(rxbuf))

		p = &pdu{
			unitId:       unitId,
			functionCode: functionCode,
			payload:      rxbuf[0:],
		}
	} else if functionCode == 0x0F || functionCode == 0x10 { // 请求：写多个线圈、写多个寄存器

		// fmt.Println("--------------15 16-------------------")

		// 5字节：线圈/寄存器的起始地址（2字节）、线圈/寄存器的数量、设置数据的字节数
		recvAddrNum := make([]byte, 5)
		_, err = io.ReadFull(tt.socket, recvAddrNum)
		if err != nil {
			return
		}
		// 打印接收的数据
		// fmt.Println(BytesToHex(recvAddrNum))

		// 写线圈/寄存器的数据长度
		bytesNum := int(recvAddrNum[4])
		// fmt.Printf("bytesNum: %d\n", bytesNum)

		recvBuffer := make([]byte, bytesNum+CRC16Length)
		_, err = io.ReadFull(tt.socket, recvBuffer)
		if err != nil {
			return
		}
		// 打印接收的数据
		// fmt.Println(BytesToHex(recvBuffer))

		modbusBuffer := make([]byte, 5+len(recvBuffer))
		copy(modbusBuffer, recvAddrNum)
		copy(modbusBuffer[5:], recvBuffer)
		// fmt.Println(BytesToHex(dataBuffer))

		p = &pdu{
			unitId:       unitId,
			functionCode: functionCode,
			payload:      modbusBuffer[0:],
		}
	}

	return
}

// Turns a PDU into an MBAP frame (MBAP header + PDU) and returns it as bytes.
func (tt *rtuOverTCPTransport) assembleMBAPFrame(txnId uint16, p *pdu) (payload []byte) {
	// transaction identifier
	// payload = uint16ToBytes(BIG_ENDIAN, txnId)

	// protocol identifier (always 0x0000)
	// payload = append(payload, 0x00, 0x00)

	// length (covers unit identifier + function code + payload fields)
	// payload = append(payload, uint16ToBytes(BIG_ENDIAN, uint16(2+len(p.payload)))...)

	// unit identifier 从站ID
	payload = append(payload, p.unitId)

	// function code 功能码
	payload = append(payload, p.functionCode)

	// payload
	payload = append(payload, p.payload...)

	// crc16 校验码
	var cc crc
	cc.init()
	cc.add(payload)

	payload = append(payload, uint16ToBytes(LITTLE_ENDIAN, cc.crc)...)

	return
}
